﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LGPeWJNM.BaseClasses;

namespace AplicacaoTeste
{
    /// <summary>
    /// Interface inicial do programa
    /// </summary>
    public partial class Form1 : Form
    {
        /// <summary>
        /// Armazena as soluções ótimas para poder salvar os resultados e gerar os gráficos.
        /// </summary>
        public IEnumerable<LinkedList<ICliente>> SolucaoOtima { get; set; }

        /// <summary>
        /// Construtor padrão
        /// </summary>
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (button1.Text == "Parar")
            {
                button1.Text = "Iniciar";
                return;
            }
            //bloqueando a interface para evitar multiplas chamadas, e dando dicas que está ocupado.
            button1.Text = "Parar";
            //desacoplando a thread da interface da thread que será utilizada para processar
            new System.Threading.Thread(() =>
            {
                //carrega a instância, está pré-definido um caminho aqui.
                var instanciaDS = new LGPeWJNM.DataAccessLayer.InstanciaDataset(@"C:\Users\l0861\Downloads\Vrp-All.zip");
                var instancia = instanciaDS[new LGPeWJNM.DataAccessLayer.IdInstancia("M", "M-n200-k17")];
                this.BeginInvoke(new MethodInvoker(() =>
                        {
                            Benchmark.Text = instancia.Otimo.ToString();
                        }));
                //inicializa as classes que "escolhi" utilizar nessa combinação.
                var medianador = new LGPeWJNM.MonteCarlo.MonteCarlo();
                var designador = new LGPeWJNM.Trunfo.Trunfo();
                var roteirizador = new LGPeWJNM.Vmp.Vmp();//new Vmp.Vmp();
                var otimizador = new LGPeWJNM._2Opt._2Opt();

                var custoTotalOtimo = double.MaxValue;
                var instanciaNr = 0;
                while (button1.Text == "Parar")
                {
                    var start = System.DateTime.Now;
                    //chama os métodos em ordem
                    var medianas = medianador.Processar(instancia.Destinos, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo);
                    var designacao = designador.Processar(medianas, instancia.Destinos, instancia.Deposito, instancia.CapacidadeVeiculo);
                    //veja que eu recupero a designação do tg (Teitz e Bart com Gillet Johnson) e passo pro vmp como parâmetro de entrada.
                    var roteirizacao = roteirizador.Processar(designacao, instancia.Deposito, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo).AsParallel();
                    //mesma coisa faço com a roteirização proviniente do vmp pra passar pro _2opt
                    var refinamento = otimizador.Processar(roteirizacao, instancia.Deposito, instancia.QuantidadeVeiculos, instancia.CapacidadeVeiculo).AsParallel();
                    var stop = System.DateTime.Now;
                    instanciaNr++;

                    var custoTotalAtual = refinamento.Select(s => s.CustoTotal()).Sum();
                    this.BeginInvoke(new MethodInvoker(() =>
                        {
                            chart1.Series["Instancia"].Points.AddXY(instanciaNr, custoTotalAtual);
                        }));
                    if (custoTotalOtimo >= custoTotalAtual)
                    {
                        custoTotalOtimo = custoTotalAtual;
                        SolucaoOtima = refinamento.ToList();
                        this.BeginInvoke(new MethodInvoker(() =>
                        {
                            SalvarOtimo.Enabled = true;
                            MelhorResultado.Text = custoTotalOtimo.ToString("F");
                            Desvio.Text = (((custoTotalOtimo / (double)instancia.Otimo) - 1) * 100).ToString("F");
                            TempoProcessamento.Text = stop.Subtract(start).TotalMilliseconds.ToString("F");

                            chart1.Series["Otimo"].Points.AddXY(instanciaNr, custoTotalOtimo);
                        }));
                    }
                }

                //solicitando para interface retornar ao estado ativo.
                this.BeginInvoke(new MethodInvoker(() =>
                {
                    button1.Text = "Iniciar";
                }));
            }).Start();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void SalvarOtimo_Click(object sender, EventArgs e)
        {
            switch (SalvarOtimoDialog.ShowDialog())
            {
                case DialogResult.Abort:
                    break;
                case DialogResult.Cancel:
                    break;
                case DialogResult.Ignore:
                    break;
                case DialogResult.No:
                    break;
                case DialogResult.None:
                    break;
                case DialogResult.Retry:
                    break;
                case DialogResult.OK:
                case DialogResult.Yes:
                    var s = SolucaoOtima.Select(s1 => s1.Select(s2 => (LGPeWJNM.BaseClasses.Cliente)s2).ToArray()).ToArray();
                    var xs = new System.Xml.Serialization.XmlSerializer(s.GetType());
                    using (var fs = new System.IO.StreamWriter(SalvarOtimoDialog.FileName))
                    {
                        xs.Serialize(fs, s);
                        fs.Flush();
                    }
                    break;
                default:
                    break;
            }
        }
    }
}